package cn.xorange.commons.security.handler;

import cn.xorange.commons.datasource.constants.DataErrorCode;
import cn.xorange.commons.security.exception.SecurityErrorCode;
import cn.xorange.commons.utils.exception.ExceptionUtils;
import cn.xorange.commons.utils.http.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Request;
import org.apache.tomcat.util.buf.MessageBytes;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author : yangjian
 *  * 认证失败处理类  401 （登录认证失败）
 *  * 用户未登录时返回给前端的数据
 *  * （用来解决匿名用户访问资源时的异常）
 */
@Slf4j
public class AuthEntryPointHandler implements AuthenticationEntryPoint, Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
        try {
            Object a1= request.getAttribute("javax.servlet.error.exception");
            if(Objects.nonNull(a1)){
                ServletUtils.responseOut(response,  SecurityErrorCode.ERROR.getStatus(),
                        SecurityErrorCode.ERROR.getCode(), String.valueOf(a1),null);
            }

            Object a = findCoyoteRequest(request);
            Field coyoteRequest = a.getClass().getDeclaredField("coyoteRequest");
            coyoteRequest.setAccessible(true);
            Object b = coyoteRequest.get(a);

            Field uriMB = b.getClass().getDeclaredField("uriMB");
            uriMB.setAccessible(true);
            MessageBytes c = (MessageBytes)uriMB.get(b);
            log.debug("AuthenticationEntryPointHandler.commence方法：未登录 url:" + c.getString());

            ServletUtils.responseOut(response, SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getStatus(),
                    SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getCode(), SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getMsg(),null);
        } catch (Throwable t) {
            ServletUtils.responseOut(response, SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getStatus(),
                    SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getCode(), SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED.getMsg(), ExceptionUtils.getCauseMsg(t));
        }
    }


    private Class getClassByName(Class classObject, String name){
        Map<Class, List<Field>> fieldMap = new HashMap<>();
        Class returnClass = null;
        Class tempClass = classObject;
        while (tempClass != null) {
            fieldMap.put(tempClass, Arrays.asList(tempClass .getDeclaredFields()));
            tempClass = tempClass.getSuperclass();
        }

        for(Map.Entry<Class,List<Field>> entry: fieldMap.entrySet()){
            for (Field f : entry.getValue()) {
                if(f.getName().equals(name)){
                    returnClass = entry.getKey();
                    break;
                }
            }
        }
        return returnClass;
    }

    //递归遍历父类寻找coyoteRequest Field
    private Object findCoyoteRequest(Object request)  throws Exception {
        Class a = getClassByName(request.getClass(),"request");
        Field request1 = a.getDeclaredField("request");
        request1.setAccessible(true);
        Object b = request1.get(request);
        if(getClassByName(b.getClass(),"coyoteRequest") == null){
            return findCoyoteRequest(b);
        }else{
            return b;
        }
    }
}
